﻿using System;
using System.Collections;
using System.Collections.Generic;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Model
{
    /// <summary>
    /// 数组数据片
    /// </summary>
    public class ArraySpan<T> : IEnumerable<T>
    {
        private T[] _array = null;
        /// <summary>
        /// 数组数据
        /// </summary>
        public T[] Array
        {
            get
            {
                return this._array;
            }
        }

        private int _offset = 0;
        /// <summary>
        /// 相对于数组或原始数据指针数据起始偏移位置
        /// </summary>
        public int Offset
        {
            get
            {
                return this._offset;
            }
        }


        private int _length = 0;
        /// <summary>
        /// 相对于数组或原始数据指针数据起始偏移位置
        /// </summary>
        public int Length
        {
            get
            {
                return this._length;
            }
        }

        /// <summary>
        /// 数据容量
        /// </summary>
        public int Capcity
        {
            get
            {
                return this._array == null ? 0 : this._array.Length;
            }
        }


        /// <summary>
        /// 获取或设置用户定义的数据
        /// </summary>
        public object Tag { get; set; }


        /// <summary>
        /// 获取或设置值
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        /// <exception cref="IndexOutOfRangeException">索引越界异常</exception>
        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引越界,index:{index},当前数据长度:{this._length}");
                }

                return this._array[this._offset + index];
            }
            set
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引越界,index:{index},当前数据长度:{this._length}");
                }

                this._array[this._offset + index] = value;
            }
        }



        /// <summary>
        /// 构造函数
        /// </summary>
        public ArraySpan()
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="capcaity">初始化容量</param>
        public ArraySpan(int capcaity)
        {
            this.Check(capcaity);
        }

        /// <summary>
        /// 构造函数-数组数据片
        /// </summary>
        /// <param name="buffer">内存数组buffer数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public ArraySpan(T[] buffer, int startIndex = -1, int length = -1)
        {
            this.Update(buffer, startIndex, length);
        }



        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void Update(int startIndex, int length)
        {
            if (startIndex < 0 || startIndex >= this._array.Length)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置不能小于0且不能大于等于容量值,startIndex无效");
            }

            if (length < 0 || length > this._array.Length)
            {
                throw new ArgumentOutOfRangeException($"长度值不能小于0且不能超出容量上限,length无效,");
            }

            if (startIndex + length > this._array.Length)
            {
                throw new ArgumentOutOfRangeException($"数据长度超出容量值.");
            }

            this._offset = 0;
            this._length = length;
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="buffer">内存数组buffer数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void Update(T[] buffer, int startIndex = -1, int length = -1)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (startIndex < 0)
            {
                startIndex = 0;
            }
            else if (startIndex >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置\"{startIndex}\"无效,超出范围");
            }

            if (length < 0)
            {
                length = buffer.Length - startIndex;
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._array = buffer;

            this._offset = startIndex;
            this._length = length;
        }


        /// <summary>
        /// 检查数据长度,当内部数组为null或数组长度小于需要数据长度时,重新创建数据[有重新创建数据返回true,未重新创建数据返回false]
        /// </summary>
        /// <param name="length">需要数据长度</param>
        /// <returns>有重新创建数据返回true,未重新创建数据返回false</returns>
        public bool Check(int length)
        {
            if (this._array == null || this._array.Length < length)
            {
                this._array = new T[length];
                this._offset = 0;
                this._length = length;
                return true;
            }
            else
            {
                this._offset = 0;
                this._length = length;
                return false;
            }
        }

        /// <summary>
        /// 获取第一项
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public T First()
        {
            return this._array[this._offset];
        }

        /// <summary>
        /// 获取最后一项
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public T Last()
        {
            return this._array[this._offset + this._length - 1];
        }

        /// <summary>
        /// 获取第一项或默认值
        /// </summary>
        /// <returns>第一项或默认值</returns>
        public T FirstOrDefault()
        {
            var index = this._offset;
            if (this._array == null || index < 0 || index >= this._array.Length)
            {
                return default(T);
            }
            else
            {
                return this._array[index];
            }
        }

        /// <summary>
        /// 获取最后一项或默认值
        /// </summary>
        /// <returns>最后一项或默认值</returns>
        public T LastOrDefault()
        {
            var index = this._offset + this._length - 1;
            if (this._array == null || index < 0 || index >= this._array.Length)
            {
                return default(T);
            }
            else
            {
                return this._array[index];
            }
        }




        /// <summary>
        /// 克隆当前对象,数据重新复制一份
        /// </summary>
        /// <returns>克隆完的数据</returns>
        public ArraySpan<T> ToDataSpan()
        {
            if (this._length == 0)
            {
                return new ArraySpan<T>(this._array, 0, 0);
            }

            T[] buffer = new T[this._length];
            System.Array.Copy(this._array, this._offset, buffer, 0, buffer.Length);
            return new ArraySpan<T>(buffer);
        }

        /// <summary>
        /// 克隆当前对象,数据重新复制一份
        /// </summary>
        /// <returns>克隆完的数据</returns>
        public ArraySpan<T> ToArraySpan()
        {
            if (this._length == 0)
            {
                return new ArraySpan<T>(this._array, 0, 0);
            }

            T[] buffer = new T[this._length];
            System.Array.Copy(this._array, this._offset, buffer, 0, buffer.Length);
            return new ArraySpan<T>(buffer);
        }

        /// <summary>
        /// 克隆当前对象,数据重新复制一份
        /// </summary>
        /// <returns>克隆完的数据</returns>
        public T[] ToArray()
        {
            if (this._length == 0)
            {
#if NET4_0
                return Base.ArrayEx.Empty<T>();
#else
                return System.Array.Empty<T>();
#endif
            }


            T[] array = new T[this._length];
            System.Array.Copy(this._array, this._offset, array, 0, array.Length);
            return array;
        }

        /// <summary>
        /// 将当前数组拷贝到目标数组
        /// </summary>
        /// <param name="destinationArray">目标数组</param>
        /// <param name="destinationIndex">目标数组起始索引</param>
        public void CopyTo(Array destinationArray, int destinationIndex)
        {
            System.Array.Copy(this._array, this._offset, destinationArray, destinationIndex, this._length);
        }

        /// <summary>
        /// 将当前数组拷贝到目标数组
        /// </summary>
        /// <param name="sourceIndex">当前数组起始索引</param>
        /// <param name="destinationArray">目标数组</param>
        /// <param name="destinationIndex">目标数组起始索引</param>
        /// <param name="length">拷贝数据长度</param>
        public void CopyTo(int sourceIndex, Array destinationArray, int destinationIndex, int length)
        {
            System.Array.Copy(this._array, this._offset + sourceIndex, destinationArray, destinationIndex, length);
        }

        /// <summary>
        /// 从源数组中拷贝数据到当前数组
        /// </summary>
        /// <param name="sourceArray">源数组</param>
        /// <param name="length">拷贝数据长度</param>
        public void CopyFrom(Array sourceArray, int length)
        {
            System.Array.Copy(sourceArray, 0, this._array, this._offset, length);
        }

        /// <summary>
        /// 从源数组中拷贝数据到当前数组
        /// </summary>
        /// <param name="sourceArray">源数组</param>
        /// <param name="sourceIndex">源数组起始索引</param>
        /// <param name="destinationIndex">当前数组起始索引</param>
        /// <param name="length">拷贝数据长度</param>
        public void CopyFrom(Array sourceArray, int sourceIndex, int destinationIndex, int length)
        {
            System.Array.Copy(sourceArray, sourceIndex, this._array, this._offset + destinationIndex, length);
        }

#if !NET4_0
        /// <summary>
        /// 获取Span数据
        /// </summary>
        /// <returns>Span数据</returns>
        public Span<T> GetSpan()
        {
            return new Span<T>(this._array, this._offset, this._length);
        }
#endif

        /// <summary>
        /// IEnumerable
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            return new PartEnumerator<T>(this._array, this._offset, this._length);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

    }
}
